﻿// <copyright file="EmailFactory.cs" company="Paxoft">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Pavel Kotlyar</author>
// <email>paxer@mail.ru</email>
// <date>2009-10-12</date>
// <summary>This class is a part of open source .Net Factory Framework for Web Developers. Make Web Dev easy and fun and reduce boring things is the main aim of this project. Project url http://factoryframework.codeplex.com/ . Licence GNU Library General Public License (LGPL)</summary>

namespace Factory.Framework
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net.Mail;
    using System.Web;
    using System.Xml.XPath;

    /// <summary>
    /// Email factory. Class provide functionality for sending emails by using templates
    /// </summary>
    public class EmailFactory : Factory
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the EmailFactory class.
        /// </summary>
        /// <param name="emailFactoryConfigFilePath">Email Factory configuration file path</param>
        public EmailFactory(string emailFactoryConfigFilePath)
        {
            this.EmailFactoryConfigPath = emailFactoryConfigFilePath;
            this.LoadEmailSettings();
        }

        #endregion

        #region Public Properties
        
        /// <summary>
        /// Gets or sets email factory settings mode, setup in config file/
        /// </summary>
        public string Mode { get; set; }
        
        /// <summary>
        /// Gets or sets smtp host address.
        /// </summary>
        public string SmtpHost { get; set; }

        /// <summary>
        /// Gets or sets Smtp server username
        /// </summary>
        public string SmtpUserName { get; set; }

        /// <summary>
        /// Gets or sets Smtp server password
        /// </summary>
        public string SmtpPassword { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether use SSL connection.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if use SSL connection; otherwise, <see langword="false"/>.
        /// </value>
        public bool UseSSL { get; set; }

        /// <summary>
        /// Gets or sets default From email address
        /// </summary>
        public string DefaultFromAddress { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this emial have HTML or plain text body.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if email body is HTML; otherwise, <see langword="false"/>.
        /// </value>
        public bool IsHtml { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this emial should be saved.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if email should be saved; otherwise, <see langword="false"/>.
        /// </value>
        public bool SaveEmailCopy { get; set; }

        /// <summary>
        /// Gets or sets emial template folder
        /// </summary>
        public string EmailTemplateFolder { get; set; }

        /// <summary>
        /// Gets or sets path where to save sent emails
        /// </summary>
        public string SendtEmailsSavePath { get; set; }

        /// <summary>
        /// Gets or sets EmailFacotry configuration file path
        /// </summary>
        public string EmailFactoryConfigPath { get; set; }

        #endregion

        #region Public Methods Section

        /// <summary>
        /// Send email
        /// </summary>
        /// <param name="listOfToAddress">Recipients email addresses </param>
        /// <param name="emailTemplateName">Email template file name</param>
        /// <param name="templateKeyValues">Pair of placeholder and value for replace in email template</param>
        /// <param name="attachemtnFilesPath">Pathes for each attached file. Set null if no attachments</param>
        /// <returns>Return false if sending email was disabled in Email.config file</returns>
        public bool SendMail(string[] listOfToAddress, string emailTemplateName, Dictionary<string, string> templateKeyValues, string[] attachemtnFilesPath)
        {
            // If sending email is enabled
            if (this.FactoryEnabled)
            {
                var mailMsg = new MailMessage();
                mailMsg.IsBodyHtml = this.IsHtml;

                // Get Email Template
                var streamReader = new StreamReader(HttpContext.Current.Server.MapPath(this.EmailTemplateFolder + emailTemplateName));

                // Read first line in template file, it should be always subject
                string emailSubject = streamReader.ReadLine().ToString();

                // Reading template body
                string emailTemplate = streamReader.ReadToEnd();
                string emailBody = emailTemplate.Replace(emailSubject, string.Empty);

                foreach (KeyValuePair<string, string> item in templateKeyValues)
                {
                    emailBody = emailBody.Replace(item.Key, item.Value);
                }

                mailMsg.From = new MailAddress(this.DefaultFromAddress);
                mailMsg.Subject = emailSubject;

                // Getting list emails to send
                string allEmailsToSend = string.Empty;
                foreach (string toEmail in listOfToAddress)
                {
                    mailMsg.To.Add(toEmail);
                    allEmailsToSend += toEmail;
                }

                // Add attachemts
                string allAttachedFilesPath = string.Empty;
                if (attachemtnFilesPath != null)
                {
                    foreach (string path in attachemtnFilesPath)
                    {
                        mailMsg.Attachments.Add(new Attachment(path));
                        allAttachedFilesPath += path;
                    }
                }

                mailMsg.Body = emailBody;

                var smtp = new SmtpClient(this.SmtpHost);
                smtp.Send(mailMsg);

                // Save email            
                if (this.SaveEmailCopy)
                {
                    this.SaveEmail(allEmailsToSend + "\n" + emailSubject + "\n" + allAttachedFilesPath + "\n" + emailBody);
                }

                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Simple Email Sending
        /// </summary>
        /// <param name="listOfToAddress">Recipients email addresses</param>
        /// <param name="emailSubject">Email subject</param>
        /// <param name="emailText">Email body</param>
        /// <returns>Is succesfully sent</returns>
        public bool SendMail(string[] listOfToAddress, string emailSubject, string emailText)
        {
            // If sending email is enabled
            if (this.FactoryEnabled)
            {
                var mailMsg = new MailMessage();
                mailMsg.IsBodyHtml = this.IsHtml;
                mailMsg.From = new MailAddress(this.DefaultFromAddress);

                // Getting list emails to send
                string allEmailsToSend = string.Empty;
                foreach (string toEmail in listOfToAddress)
                {
                    mailMsg.To.Add(toEmail);
                    allEmailsToSend += toEmail;
                }

                mailMsg.Subject = emailSubject;
                mailMsg.Body = emailText;

                var smtp = new SmtpClient(this.SmtpHost);
                smtp.Send(mailMsg);

                return true;
            }
            else
            {
                return false;
            }
        }
        #endregion

        #region Private Methods Section

        /// <summary>
        /// Save email content to disk
        /// </summary>
        /// <param name="emailContent">Email body</param>
        private void SaveEmail(string emailContent)
        {
            var rnd = new Random();
            int rndNumber = rnd.Next(10000, 99999999);
            string emailContentFileName = DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + Convert.ToString(rndNumber) + ".email";
            StreamWriter streamWriter;
            streamWriter = File.CreateText(HttpContext.Current.Server.MapPath(this.SendtEmailsSavePath + emailContentFileName));
            streamWriter.Write(emailContent);
            streamWriter.Close();
        }
        #endregion

        #region Initialization Section

        /// <summary>
        /// Initialize Email.config settings
        /// </summary>
        private void LoadEmailSettings()
        {
            var emailSettings = new Dictionary<string, string>();
            var doc = new XPathDocument(HttpContext.Current.Server.MapPath(this.EmailFactoryConfigPath));
            XPathNavigator navigator = doc.CreateNavigator();

            this.EmailTemplateFolder = navigator.SelectSingleNode("/emailFactorySettings/@emailTemplateFolder").Value;
            this.SendtEmailsSavePath = navigator.SelectSingleNode("/emailFactorySettings/@sentEmailsSavePath").Value;
            bool autoLoadSettings = Convert.ToBoolean(navigator.SelectSingleNode("/emailFactorySettings/@autoMode").Value);

            // check for AutoLoad Settings mode
            if (autoLoadSettings)
            {
                string currentHost = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);

                XPathNodeIterator modesList = navigator.Select("/emailFactorySettings/mode/*");

                // go through all modes and check url setting
                while (modesList.MoveNext())
                {
                    string mode = modesList.Current.Name;
                    string modeUrl = Convert.ToString(navigator.SelectSingleNode("/emailFactorySettings/mode/" + mode + "/url").Value);

                    // Compare with current host url and set mode
                    if (modeUrl == currentHost)
                    {
                        this.Mode = mode;
                    }
                }
            }
            else
            {
                // If mode does not initialized by constructor then reading default settings mode
                if (String.IsNullOrEmpty(this.Mode))
                {
                    this.Mode = navigator.SelectSingleNode("/emailFactorySettings/@mode").Value;
                }
            }

            XPathNodeIterator iterator = navigator.Select("/emailFactorySettings/mode/" + this.Mode + "/*");

            while (iterator.MoveNext())
            {
                emailSettings.Add(iterator.Current.Name, iterator.Current.Value);
            }

            this.FactoryEnabled = Convert.ToBoolean(emailSettings["enable"]);
            this.SmtpHost = emailSettings["smtpHost"].ToString();
            this.SmtpUserName = emailSettings["smtpUserName"].ToString();
            this.SmtpPassword = emailSettings["smtpPassword"].ToString();
            this.UseSSL = Convert.ToBoolean(emailSettings["useSSL"]);
            this.DefaultFromAddress = emailSettings["defaultFromAddress"].ToString();
            this.IsHtml = Convert.ToBoolean(emailSettings["html"]);
            this.SaveEmailCopy = Convert.ToBoolean(emailSettings["saveEmailCopy"]);
        }

        #endregion
    }
}